/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {
  ArkMethod,
  Stmt,
  ArkAssignStmt,
  ArkNormalBinopExpr,
  Value,
  AbstractInvokeExpr,
} from "arkanalyzer/lib";
import { ArkClass } from "arkanalyzer/lib/core/model/ArkClass";
import Logger, { LOG_MODULE_TYPE } from "arkanalyzer/lib/utils/logger";
import { BaseChecker, BaseMetaData } from "../../checker/BaseChecker";
import { Defects, IssueReport } from "../../model/Defects";
import {
  ClassMatcher,
  MatcherCallback,
  MatcherTypes,
} from "../../matcher/Matchers";
import { RuleListUtil } from "../../utils/common/DefectsList";
import { CheckerUtils } from "../../utils/checker/CheckerUtils";
import { Rule } from "../../model/Rule";

const logger = Logger.getLogger(
  LOG_MODULE_TYPE.HOMECHECK,
  "NoUnusedExpressionsCheck"
);
const gMetaData: BaseMetaData = {
  severity: 1,
  ruleDocPath: "docs/no-unused-expressions-check.md", // TODO: support url
  description:
    "Expected an assignment or function call and instead saw an expression.",
};
type Scene_base = {
  declaringStmt?: Stmt;
  name?: string;
  usedStmts?: Stmt[];
};
//结果类型
enum ExpType {
  Tag = "tag",
  allowShortCircuit = "allowShortCircuit",
  allowTernary = "allowTernary",
  allowTaggedTemplates = "allowTaggedTemplates",
}
type NoUsedExpression = {
  stmt: Stmt;
  originalText: string;
  exptype?: ExpType;
};
type AllowOptionEntries = {
  allowShortCircuit?: boolean;
  allowTaggedTemplates?: boolean;
  allowTernary?: boolean;
};

interface Options {
  allow: AllowOptionEntries;
}
const defaultOptions: Options = {
  allow: {
    allowShortCircuit: false,
    allowTaggedTemplates: false,
    allowTernary: false,
  },
};
export class NoUnusedExpressionsCheck implements BaseChecker {
  readonly metaData: BaseMetaData = gMetaData;
  readonly FOREACH_STR: string = "ForEach";
  readonly CREAER_STR: string = "create";
  public rule: Rule;
  public defects: Defects[] = [];
  public issues: IssueReport[] = [];
  private clsMatcher: ClassMatcher = {
    matcherType: MatcherTypes.CLASS,
  };
  public registerMatchers(): MatcherCallback[] {
    const matchBuildCb: MatcherCallback = {
      matcher: this.clsMatcher,
      callback: this.check,
    };

    return [matchBuildCb];
  }

  public check = (arkClass: ArkClass) => {
    const severity = this.rule.alert ?? this.metaData.severity;
    this.arkFieldExpressionsProcess(arkClass, severity);
  };

  //类体
  private arkFieldExpressionsProcess(
    arkClass: ArkClass,
    severity: number
  ): void {
    const options: Options = this.rule.option as unknown as Options;
    const mergedOptions: Options = {
      ...defaultOptions,
      ...options,
    };
    const arkMeths: ArkMethod[] = arkClass.getMethods();
    for (const arkMethod of arkClass.getMethods()) {
      const stmts = arkMethod.getBody()?.getCfg().getStmts() ?? []; //获取方法体的所有语句()
      //执行体
      this.expressionsProcess(stmts, mergedOptions);
    }
  }
  //方法体
  expressionsProcess(stmts: Stmt[], mergedOptions: Options): void {
    let noUsedExpression: NoUsedExpression[] = [];
    let textTernary = ""; //存放三元表达式信息---->锁定三元表达相关语句;
    let textTernaryBefor = ""; //存放三元表达式信息---->锁定三元表达相关语句;
    let textCircuit = ""; //存放逻辑运算信息信息---->锁定|| &&相关语句;
    let textCircuitBefor = ""; //存放逻辑运算信息信息---->锁定|| &&相关语句;
    let TernaryErrorCount = 0;
    let CircuitErrorCount = 0;
    let prevStm: Stmt = stmts[0];
    for (const index in stmts) {
      const stm = stmts[index];
      const nowline = stm.getOriginPositionInfo().getLineNo() ?? -1;
      const originalText = stm.getOriginalText() ?? "";
      const isValidTernary = this.isValidTernary(originalText);

      let exists = this.checkExpression(noUsedExpression, nowline);

      // Handle Ternary Expressions
      if (textTernary != "" && originalText != textTernary) {
        textTernaryBefor = textTernary;
      }
      if (textCircuit != "" && originalText != textCircuit) {
        textCircuitBefor = textCircuit;
      }
      if (textTernary == "" && isValidTernary) {
        textTernary = originalText;
      }

      if (textTernaryBefor !== "") {
        exists = this.checkExpression(noUsedExpression, nowline);
        if (!exists && TernaryErrorCount === 0) {
          noUsedExpression.push({
            stmt: prevStm,
            originalText: textTernaryBefor,
            exptype: ExpType.allowTernary,
          });
        } else {
          noUsedExpression.push({
            stmt: prevStm,
            originalText: textTernaryBefor,
          });
        }
        textTernary = "";
        textTernaryBefor = "";
        TernaryErrorCount = 0;
      }

      if (textCircuitBefor !== "") {
        exists = this.checkExpression(noUsedExpression, nowline);
        if (!exists && CircuitErrorCount === 0) {
          noUsedExpression.push({
            stmt: prevStm,
            originalText: textCircuitBefor,
            exptype: ExpType.allowShortCircuit,
          });
        } else {
          noUsedExpression.push({
            stmt: prevStm,
            originalText: textCircuitBefor,
          });
        }
        textCircuit = "";
        textCircuitBefor = "";
        CircuitErrorCount = 0;
      }

      // Handle ArkAssignStmt (Assignment Statements)
      if (stm instanceof ArkAssignStmt) {
        prevStm = stm; //记录上一层
        const leftOp = stm.getLeftOp();
        const rightOp = stm.getRightOp();
        if (leftOp.toString() == "this") {
          continue;
        }
        const currentOriginalText = originalText;
        const isCircuit = this.isCircuit(rightOp);
        // Check for Circuit Expressions

        if (textCircuit == "" && isCircuit) {
          textCircuit = currentOriginalText;
        }
        // Handle changes to Ternary Expression

        // Ternary Expression Error Handling
        if (textTernary !== "" && currentOriginalText === textTernary) {
          let isValidTernary;

          if (leftOp.toString().startsWith("%")) {
            isValidTernary = this.allowTernaryExpressions(rightOp);
          } else {
            isValidTernary = true;
          }
          if (!isValidTernary) {
            TernaryErrorCount++;
          }
        }

        // Circuit Expression Error Handling
        if (
          textCircuit === currentOriginalText &&
          !this.allowShortCircuitExpressions(rightOp)
        ) {
          CircuitErrorCount += 1; // Update textCircuit count (unclear intent, but preserving original behavior)
        }
        // Handle No Expression Check
        exists = this.checkExpression(noUsedExpression, nowline);
        if (
          !exists &&
          !isCircuit &&
          !isValidTernary &&
          this.isNoExpression(stm)
        ) {
          //过滤语句 执行语句 场景一
          if (this.isAssignDefaultStatement(currentOriginalText)) {
            continue;
          }
          noUsedExpression.push({
            stmt: stm,
            originalText: currentOriginalText,
          });
        }
        //排除最后还有语句
        if (Number(index) === stmts.length - 1) {
          if (textTernaryBefor !== "") {
            exists = this.checkExpression(noUsedExpression, nowline);
            if (!exists && TernaryErrorCount === 0) {
              noUsedExpression.push({
                stmt: stm,
                originalText: textTernary,
                exptype: ExpType.allowTernary,
              });
            } else {
              noUsedExpression.push({
                stmt: stm,
                originalText: textTernary,
              });
            }
            textTernary = "";
            textTernaryBefor = "";
            TernaryErrorCount = 0;
          }

          if (textCircuitBefor !== "") {
            exists = this.checkExpression(noUsedExpression, nowline);
            if (!exists && CircuitErrorCount === 0) {
              noUsedExpression.push({
                stmt: stm,
                originalText: textCircuit,
                exptype: ExpType.allowShortCircuit,
              });
            } else {
              noUsedExpression.push({
                stmt: stm,
                originalText: textCircuit,
              });
            }
            textCircuit = "";
            textCircuitBefor = "";
            CircuitErrorCount = 0;
          }
        }
      }

      // Handle Invocation Statements
      const invockeStmt = CheckerUtils.getInvokeExprFromAwaitStmt(stm);
      if (invockeStmt) {
        const args = invockeStmt.getArgs();
        const methodName = invockeStmt
          .getMethodSignature()
          .getMethodSubSignature()
          .getMethodName();

        // Check for tag expressions
        if (!exists && args.some((item) => item.toString().startsWith("%"))) {
          noUsedExpression.push({
            stmt: stm,
            originalText: originalText,
            exptype: ExpType.Tag,
          });
        }
      }

      // Final Issue Report
    }
    noUsedExpression = this.filterUnuseds(mergedOptions, noUsedExpression);
    //装配报错信息
    for (const noUsed of noUsedExpression) {
      this.addIssueReport(noUsed.stmt, noUsed.originalText);
    }
  }

  private checkExpression(
    noUsedExpression: NoUsedExpression[],
    nowline: number
  ) {
    for (let i = 0; i < noUsedExpression.length; i++) {
      const item = noUsedExpression[i];
      const lineNo = item.stmt.getOriginPositionInfo().getLineNo() ?? -1;
      if (lineNo == nowline) {
        return true;
      }
    }
    return false;
  }
  //判断是否是未使用
  private isNoExpression(stm: ArkAssignStmt): boolean {
    const leftOp = stm.getLeftOp() as Scene_base;
    if (leftOp.name?.startsWith("%") && leftOp.usedStmts?.length == 0) {
      return true;
    }
    return false; // Return false if the condition is not met.
  }

  isCircuit(rightOp: Value): Boolean {
    if (rightOp instanceof ArkNormalBinopExpr) {
      let Operator = rightOp.getOperator();
      let Op2 = rightOp.getOp2() as Scene_base;
      Op2.declaringStmt;
      if (Operator === "&&" || Operator === "||") {
        return true;
      }
    }

    return false;
  }
  allowShortCircuitExpressions(rightOp: Value): Boolean {
    if (rightOp instanceof ArkNormalBinopExpr) {
      let Operator = rightOp.getOperator();

      let Op2 = rightOp.getOp2() as Scene_base;
      Op2.declaringStmt;
      let isExpression = false;
      if (
        Op2.declaringStmt instanceof ArkAssignStmt &&
        !this.isNoExpression(Op2.declaringStmt)
      ) {
        isExpression = true;
      }
      if (
        (Operator === "&&" || Operator === "||") &&
        Op2.declaringStmt &&
        (CheckerUtils.getInvokeExprFromStmt(Op2.declaringStmt) || isExpression)
      ) {
        return true;
      }
    }

    return false;
  }

  allowTernaryExpressions(Op: any): Boolean {
    const rightOrLeft = Op as Scene_base;
    let isExpression = false;
    if (
      rightOrLeft.declaringStmt instanceof ArkAssignStmt &&
      !this.isNoExpression(rightOrLeft.declaringStmt)
    ) {
      isExpression = true;
    }
    if (Op instanceof AbstractInvokeExpr || isExpression) {
      return true;
    }
    return false;
  }
  isValidTernary(expression: string): boolean {
    // 用于存储问号和冒号的索引
    const stack: string[] = [];
    let questionIndex = -1;
    let colonIndex = -1;

    // 遍历表达式，记录问号和冒号的索引
    for (let i = 0; i < expression.length; i++) {
      const char = expression[i];
      if (char === "?") {
        if (stack.length === 0) {
          questionIndex = i;
        }
        stack.push("?");
      } else if (char === ":") {
        if (stack.length === 1) {
          colonIndex = i;
        }
        stack.pop();
      }
    }

    // 如果没有问号或冒号，表示没有有效的三元表达式
    if (questionIndex === -1 || colonIndex === -1) {
      return false;
    }

    // 获取问号之前、问号之后到冒号之前、冒号之后的表达式
    const beforeQuestion = expression.substring(0, questionIndex).trim();
    const betweenQuestionAndColon = expression
      .substring(questionIndex + 1, colonIndex)
      .trim();
    const afterColon = expression.substring(colonIndex + 1).trim();

    // 如果任何一个部分为空，表示表达式不完整
    if (!beforeQuestion || !betweenQuestionAndColon || !afterColon) {
      return false;
    }

    return true;
  }

  // 判断条件部分是否有效（可以根据具体的条件定义来进一步扩展）
  isValidCondition(expression: string): boolean {
    // 简单示范：可以根据需要添加更复杂的条件判断逻辑
    return /^[a-zA-Z0-9_]+$/.test(expression); // 这里只是一个示例
  }

  // 判断普通的表达式是否有效
  isValidExpression(expression: string): boolean {
    const assignmentPattern =
      /^\(?[a-zA-Z_][a-zA-Z0-9_]*\s*=\s*[a-zA-Z_][a-zA-Z0-9_]*\)?$/;

    // 匹配有效的函数调用，例如 "f()" 或 "f(a, b)"
    const functionCallPattern =
      /^[a-zA-Z_][a-zA-Z0-9_]*\s*\([a-zA-Z0-9_,\s]*\)$/;

    // 判断是否是有效的赋值表达式或函数调用
    return (
      assignmentPattern.test(expression) || functionCallPattern.test(expression)
    );
  }

  //检查三元表达方程执行符串是否是函数
  isPressfunc(expression: string): boolean {
    // 检查一个表达式是否可能是函数调用
    function isFunction(expr: string): boolean {
      return /^[a-zA-Z]\w*\s*\($/.test(expr.trim());
    }

    // 分割表达式并检查每个部分
    function splitAndCheck(expr: string): boolean {
      // 查找第一个问号和对应的冒号
      const questionIndex = expr.indexOf("?");
      const colonIndex = expr.indexOf(":");

      // 如果没有问号或冒号，表示没有更多的三元表达式
      if (questionIndex === -1 || colonIndex === -1) {
        return false;
      }

      // 检查问号是否在冒号之前
      if (questionIndex > colonIndex) {
        return false;
      }

      // 获取问号之前、问号之后到冒号之前、冒号之后的表达式
      const beforeQuestion = expr.substring(0, questionIndex).trim();
      const betweenQuestionAndColon = expr
        .substring(questionIndex + 1, colonIndex)
        .trim();
      const afterColon = expr.substring(colonIndex + 1).trim();

      // 检查问号之后和冒号之后的表达式是否是函数
      if (!isFunction(betweenQuestionAndColon) || !isFunction(afterColon)) {
        return false;
      }

      // 递归检查问号之前的表达式``
      return splitAndCheck(beforeQuestion);
    }

    // 开始递归检查整个表达式
    return splitAndCheck(expression);
  }
  //allowTaggedTemplates--->true
  isTaggedString(expression: string) {
    // 正则表达式解释：
    // ^[a-zA-Z] - 字符串以一个字母开始
    // \\s+ - 后面跟着一个或多个空格
    // " - 然后是一个双引号
    // .* - 引号内可以包含任意字符
    // "$ - 以双引号结束
    const pattern = /^[a-zA-Z]\s+".*`$/;
    return pattern.test(expression);
  }
  private filterUnuseds(mergedOptions: Options, unuseds: NoUsedExpression[]) {
    //开启检查
    //1.本地检查

    if (mergedOptions.allow.allowShortCircuit === true) {
      unuseds = unuseds.filter(
        (item) => !(item.exptype === ExpType.allowShortCircuit)
      );
    }
    //1.1是否过滤变量
    if (mergedOptions.allow.allowTaggedTemplates === true) {
      unuseds = unuseds.filter(
        (item) => !(item.exptype === ExpType.allowTaggedTemplates)
      );
    }
    //2.函数参数 (忽略最后调用之前--默认值)
    if (mergedOptions.allow.allowTernary === true) {
      unuseds = unuseds.filter(
        (item) => !(item.exptype === ExpType.allowTernary)
      );
    }

    return unuseds;
  }

  //判断是否是赋值语句
  public isAssignDefaultStatement(text: string): boolean {
    let containsInvalidChars = false;
    if (text.length === 0) {
      return true;
    }
    // 正则表达式用于检查字符串是否包含赋值操作符（=），
    // 确保赋值操作符的左边不是纯数字，
    //     ^：字符串的开始。
    // (?!\d+=)：负向前瞻断言，确保字符串不是以数字后跟等号开始的。
    // (?: ... )：非捕获组，用于组合多个选择。
    // (?:[^=\d]*=\s*)?：匹配零个或多个不是等号或数字的字符，后跟一个等号和任意数量的空白字符。整个组是可选的，以便 “delete” 可以在字符串的开始位置。
    // delete\s+：匹配 “delete” 字符串后跟至少一个空格。
    // .*：匹配任意数量的任意字符（除了换行符）。
    // |：逻辑“或”操作符，用于分隔两个选择。
    // [^=\d]+=.*$：匹配不是等号或数字的字符后跟等号，然后是任意数量的任意字符直到字符串的末尾。
    // (?:var|let|const)\s+：匹配 var、let 或 const 后跟至少一个空格。  const pattern = /^(?!\d+=)(?:(?:[^=\d]*=\s*)?delete\s+.*|[^=\d]+=.*$)/;
    const pattern =
      /^(?!\d+=)(?:(?:[^=\d]*=\s*)?delete\s+.*|[^=\d]+=.*$|(?:var|let|void|const)\s+\w+.*$)/;

    containsInvalidChars = pattern.test(text);
    return containsInvalidChars;
  }

  private addIssueReport(stmt: Stmt, name: string) {
    const severity = this.rule.alert ?? this.metaData.severity;
    const warnInfo = this.getLineAndColumn(stmt, name);
     const defect= new Defects(
        warnInfo.line,
        warnInfo.startCol,
        warnInfo.endCol,
        this.metaData.description,
        severity,
        this.rule.ruleId,
        warnInfo.filePath,
        this.metaData.ruleDocPath,
        true,
        false,
        false
      )
    this.defects.push(
      defect
    );
    this.issues.push(new IssueReport(defect, undefined));
    RuleListUtil.push(defect);
  }

  private getLineAndColumn(stmt: Stmt, name: string) {
    const originPosition = stmt.getOriginPositionInfo();
    const line = originPosition.getLineNo();
    const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
    if (arkFile) {
      const originText = stmt.getOriginalText() ?? "";
      let startCol = originPosition.getColNo();
      const pos = originText.indexOf(name);
      if (pos !== -1) {
        startCol += pos;
        const endCol = startCol + name.length - 1;
        const originPath = arkFile.getFilePath();
        return { line, startCol, endCol, filePath: originPath };
      }
    } else {
      logger.debug("originStmt or arkFile is null");
    }
    return { line: -1, startCol: -1, endCol: -1, filePath: "" };
  }
}
